#include "includes.h"
#include "structs.h"
#include "declares.h"

const char *argp_program_version = GRAPE_VERSION_;    
const char *argp_program_bug_address = MY_BUG_ADDR_;


/* The options we understand. */
static struct argp_option options[] = {
        {"verbose",  'v', 0,      0,  "Produce verbose output (very very verbose)"},
        {"quiet",    'q', 0,      0,  "Don't produce any output" },
        {"silent",   's', 0,      OPTION_ALIAS },
        {"output",   'o', "FILE", 0, "result file (default: vfile.algorithm.query_N.dat)" },


        {"vfile", 'E',"FILE",0, "vertex file"},
        {"efile", 'e',"FILE",0,"edge file"},
        {"rfile", 'r',"FILE",0,"partitioning file (if not given, generates a gpmetis input file based on vfile and efile"},
        {"nfrag", 'n',"N",0,"number of fragments"},
        {"ncon", 'c',"N",0,"number of constrains on a vertex"},
        {"query", 'Q', "FILE",0, "algorithm-specified query file"},
        {"max-step", 'm', "N", 0, "limit for the super step (default: 100)"},
        {"serial", 'S', 0, 0, "do not use pthread for parallel acceleration (default: false)"},
        { 0 }
};

/* Parse a single option. */
        static error_t
parse_opt (int key, char *arg, struct argp_state *state)
{
        /* Get the input argument from argp_parse, which we
         *      know is a pointer to our arguments structure. */
        struct arguments *arguments = state->input;
        switch (key)
        {
                case 'S':
                        arguments->parallel=0;
                        break;

                case 'q': case 's':
                        arguments->silent = 1;
                        break;
                case 'v':
                        arguments->verbose = 1;
                        break;
                case 'o':
                        arguments->output_file = arg;
                        break;
                case 'e':
                        arguments->efile       = arg;
                        break;
                case 'r':
                        arguments->rfile= arg;
                        break;

                case 'Q':
                        arguments->query_file= arg;
                        break;
                case 'E':
                        arguments->vfile= arg;
                        break;
                case 'c':
                        arguments->ncon= atoi(arg);
                        if(arguments->ncon <= 0)
                                argp_error(state,"%s: Invalid ncon %d\n", grape_app.app_name, arguments->ncon);
                        break;
                case 'm':
                        arguments->max_step= atoi(arg);
                        if(arguments->max_step <= 0)
                                argp_error(state,"%s: Invalid max_step %d\n", grape_app.app_name, arguments->max_step);
                        break;
                case 'n':
                        arguments->nfrag= atoi(arg);
                        if(arguments->nfrag <= 0)
                                argp_error(state,"%s: Invalid nfrag %d\n", grape_app.app_name, arguments->nfrag);
                        if(arguments->nfrag > N_FRAG)
                                argp_error(state,"%s: currently GRAPE does not support more than %d fragments\n", grape_app.app_name, N_FRAG); 
                        break;
                case ARGP_KEY_ARG:
                case ARGP_KEY_END:
                        break;

                default:
                        return ARGP_ERR_UNKNOWN;
        }
        return 0;
}

/* Default values. */
struct arguments arguments=
{
        .silent=0,
        .verbose=0,
        .ncon=1,
        .nfrag=4,
        .parallel=1,
        .max_step=grape_MAX_STEP
};

void report_run_conf()
{

        zlog_info(grape_proc_logger, "-----------------------------------------------------------------");
        zlog_info(grape_proc_logger, "Configuration for this run:");
        zlog_info(grape_proc_logger, "algorithm = %s"     ,grape_app.app_name);
        zlog_info(grape_proc_logger, "vfile = %s"     ,arguments.vfile);
        zlog_info(grape_proc_logger,"efile = %s"      ,arguments.efile);
        zlog_info(grape_proc_logger,"rfile = %s"    ,arguments.rfile);
        zlog_info(grape_proc_logger,"query = %s" ,arguments.query_file);

        zlog_info(grape_proc_logger,"nfrag = %d"    ,arguments.nfrag);
        zlog_info(grape_proc_logger,"ncon  = %d"    ,arguments.ncon);
        zlog_info(grape_proc_logger,"max-step = %d"       ,arguments.max_step);
        zlog_info(grape_proc_logger,"output-file = %s"   ,arguments.output_file);
        zlog_info(grape_proc_logger,"verbose = %d" ,arguments.verbose);
        zlog_info(grape_proc_logger,"silent = %d" ,arguments.silent);
        zlog_info(grape_proc_logger, "-----------------------------------------------------------------");
}

void printl_cmdargs()
{

        zlog_debug(grape_inter_logger, "vfile = %s\n"     ,arguments.vfile);
        zlog_debug(grape_inter_logger,"efile = %s\n"      ,arguments.efile);
        zlog_debug(grape_inter_logger,"rfile = %s\n"    ,arguments.rfile);
        zlog_debug(grape_inter_logger,"nfrag = %d\n"    ,arguments.nfrag);
        zlog_debug(grape_inter_logger,"ncon  = %d\n"    ,arguments.ncon);
        zlog_debug(grape_inter_logger,"mstep = %d\n"       ,arguments.max_step);
        zlog_debug(grape_inter_logger,"outfi = %s\n"   ,arguments.output_file);
        zlog_debug(grape_inter_logger,"query = %s\n" ,arguments.query_file);
        zlog_debug(grape_inter_logger,"verbo = %d\n" ,arguments.verbose);
}

/* Our argp parser. */
static struct argp argp = { options, parse_opt };

void grape_errexit_if(int cond, int flag, char *fmt, ...)
{
        if(cond) {
                fprintf(stderr,"%s: ", grape_app.app_name);
                va_list ap;
                va_start(ap,fmt);
                vfprintf(stderr,fmt,ap);
                va_end(ap);
                exit(flag);
        }
}

int grape_argp_parse(int argc, char **argv, char *doc, char *arg_doc)
{
        grape_app.app_name=getbasename(argv[0]);
        grape_app.arg_doc=arg_doc;
        grape_app.doc=doc;
        argp.args_doc=arg_doc;
        argp.doc=doc; 
        argp_parse (&argp, argc, argv, 0, 0, &arguments);

        int rc = zlog_init(ZLOG_CONF_PATH);
        grape_errexit_if(rc, 2, "zlog initilization failed\n");

        // inter_logger use silent mode by default
        grape_inter_logger=zlog_get_category("intermedia_s");
        // proc_logger use normal mode by default
        grape_proc_logger=zlog_get_category("process_n"); 

        if(arguments.verbose) {
                grape_inter_logger=zlog_get_category("intermedia_v");
                // will log a lot
        }
        else if(arguments.silent) {
                grape_proc_logger=zlog_get_category("process_s");
                // log all stuffs to file
        }
        else {
                // use normal logging
        }

        grape_data.nfrag=arguments.nfrag;
        grape_data.ncon=arguments.ncon;
        grape_data.max_step=arguments.max_step;
        grape_data.parallel=arguments.parallel;

        grape_errexit_if(!arguments.vfile, 2, GRAPE_ENOOP, "vfile");
        grape_file.vfile=fopen(arguments.vfile, "r");
        grape_errexit_if(!grape_file.vfile, 1, GRAPE_EFILE, "vfile", arguments.vfile, "reading");


        grape_errexit_if(!arguments.efile, 2, GRAPE_ENOOP, "efile");
        grape_file.efile=fopen(arguments.efile, "r");
        grape_errexit_if(!grape_file.efile, 1, GRAPE_EFILE, "efile", arguments.efile, "reading");

        grape_errexit_if(!arguments.rfile, 2, GRAPE_ENOOP, "rfile");
        grape_file.rfile=fopen(arguments.rfile, "r");
        grape_errexit_if(!grape_file.rfile, 1, GRAPE_EFILE, "rfile", arguments.rfile, "reading");


        grape_errexit_if(!arguments.query_file, 2, GRAPE_ENOOP, "query_file");
        grape_file.query_file=fopen(arguments.query_file, "r");
        grape_errexit_if(!grape_file.query_file, 1, GRAPE_EFILE, "query file", arguments.query_file, "reading");


        if(!arguments.output_file) {
                arguments.output_file=getbasename(arguments.vfile);
        }


        printl_cmdargs();
        return 0;
}
